home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / Direct3D / StateManager / renderables.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  34.0 KB  |  804 lines

  1. //--------------------------------------------------------------------------------------
  2. // File: renderables.cpp
  3. //
  4. // Material Management classes for EffectStateManager 'StateManager' Sample
  5. // These classes serve in support of the sample, but are not directly related to the
  6. // intended topic of the ID3DXEffectStateManager Interface.
  7. //
  8. // Copyright (c) Microsoft Corporation. All rights reserved.
  9. //--------------------------------------------------------------------------------------
  10.  
  11. #include "dxstdafx.h"
  12. #include "renderables.h"
  13.  
  14.  
  15.  
  16. //--------------------------------------------------------------------------------------
  17. // Resource Maps for Texture, Effect, and Mesh resources
  18. // These maps contain the set of loaded resources.
  19. //--------------------------------------------------------------------------------------
  20. CResource<LPDIRECT3DBASETEXTURE9>::resourceMap CResource<LPDIRECT3DBASETEXTURE9>::m_mapLoaded;
  21. CResource<LPD3DXEFFECT>::resourceMap           CResource<LPD3DXEFFECT>          ::m_mapLoaded;
  22. CResource<LPD3DXMESH>::resourceMap             CResource<LPD3DXMESH>            ::m_mapLoaded;
  23.  
  24.  
  25. //--------------------------------------------------------------------------------------
  26. // Creates and loads a texture from the specified filename
  27. // If a texture by the same name has already been loaded, it will be returned and its
  28. // reference count incremented.
  29. // Otherwise, the texture will be created and loaded.
  30. //--------------------------------------------------------------------------------------
  31. HRESULT CTexture::Create( LPDIRECT3DDEVICE9 pDevice, LPCWSTR wszFilename, CTexture** ppTexture )
  32. {
  33.     assert( ppTexture );
  34.     *ppTexture = NULL;
  35.  
  36.     // getExisting() will return any previously-loaded instance of this resource,
  37.     // with it's reference count incremented.
  38.     if( CTexture* pTexture = static_cast<CTexture*>( getExisting( wszFilename ) ) )
  39.     {
  40.         *ppTexture = pTexture;
  41.         return S_OK;
  42.     }
  43.  
  44.     HRESULT hr = S_OK;
  45.     D3DXIMAGE_INFO image_info;
  46.     LPDIRECT3DBASETEXTURE9 pBaseTexture = NULL;
  47.     memset( &image_info, 0, sizeof image_info );
  48.  
  49.     WCHAR wszPath[MAX_PATH];
  50.     V_RETURN( DXUTFindDXSDKMediaFileCch( wszPath, MAX_PATH, wszFilename ) );
  51.  
  52.     // The texture-type must be determined prior to loading it
  53.     V_RETURN( D3DXGetImageInfoFromFile( wszPath, &image_info ) );
  54.  
  55.     // 2D Textures
  56.     if( D3DRTYPE_TEXTURE == image_info.ResourceType )
  57.     {
  58.         LPDIRECT3DTEXTURE9 pTexture = NULL;
  59.  
  60.         V_RETURN( D3DXCreateTextureFromFile( pDevice, wszPath, &pTexture ) );
  61.  
  62.         pBaseTexture = pTexture;
  63.     }
  64.     // Cube maps
  65.     else if( D3DRTYPE_CUBETEXTURE == image_info.ResourceType )
  66.     {
  67.         LPDIRECT3DCUBETEXTURE9 pTexture = NULL;
  68.  
  69.         V_RETURN( D3DXCreateCubeTextureFromFile( pDevice, wszPath, &pTexture ) );
  70.  
  71.         pBaseTexture = pTexture;
  72.     }
  73.  
  74.     // Create a CTexture resource type
  75.     // The CTexture class will be responsible for tracking this texture,
  76.     // such that it can be notified during lost device, etc events
  77.     *ppTexture = new CTexture( wszFilename, pBaseTexture );
  78.  
  79.     return NULL != *ppTexture ? S_OK : E_OUTOFMEMORY;
  80. }
  81.  
  82.  
  83. //--------------------------------------------------------------------------------------
  84. // Texture Lost-Device Entry Point
  85. // This entry point allows textures to take actions necessary prior to a Device-Reset,
  86. // such as releasing any D3DPOOL_DEFAULT resources.
  87. // Following a lost-device event, each texture within the map of loaded textures
  88. // (CResource::m_mapLoaded) will have this entry point called.
  89. // CResource::LostDevice() is responsible for enumerating each texture and calling this
  90. // entry point.
  91. //--------------------------------------------------------------------------------------
  92. HRESULT CTexture::OnLostDevice()
  93. {
  94.     // D3DPOOL_MANAGED textures need not implement special lost device handling
  95.     return S_OK;
  96. }
  97.  
  98.  
  99. //--------------------------------------------------------------------------------------
  100. // Texture Reset-Device Entry Point
  101. // This entry point allows textures to take actions necessary following a Device-Reset,
  102. // such as re-creating any D3DPOOL_DEFAULT resources.
  103. // Following a reset-device event, each texture within the map of loaded textures
  104. // (CResource::m_mapLoaded) will have this entry point called.
  105. // CResource::ResetDevice() is responsible for enumerating each texture and calling this
  106. // entry point.
  107. //--------------------------------------------------------------------------------------
  108. HRESULT CTexture::OnResetDevice()
  109. {
  110.     // D3DPOOL_MANAGED textures need not implement special reset handling
  111.     return S_OK;
  112. }
  113.  
  114.  
  115. //--------------------------------------------------------------------------------------
  116. // Each effect may specify whether or not it wants to use the texture as a normal map.
  117. // In this case, the original texture is considered to be a height map and must be
  118. // converted prior to use.
  119. // In this sample, all normal maps are generated in a consistent manner.  For this
  120. // reason, the original texture caches any children that have been created as normal maps.
  121. // This ensures that multiple copies of the same converted texture are not allowed
  122. // to proliferate.
  123. //--------------------------------------------------------------------------------------
  124. HRESULT CTexture::GetNormalMapPtr(LPDIRECT3DBASETEXTURE9* ppTexture)
  125. {
  126.     HRESULT hr;
  127.     assert( ppTexture );
  128.     *ppTexture = NULL;
  129.  
  130.     // 
  131.     if( D3DRTYPE_TEXTURE != m_pResource->GetType() )
  132.         return E_FAIL;
  133.  
  134.     // if the normal map has already been generated, return it.
  135.     if( m_pNormalMap )
  136.     {
  137.         *ppTexture = m_pNormalMap;
  138.         return S_OK;
  139.     }
  140.  
  141.     // A normal map must be created
  142.     LPDIRECT3DDEVICE9 pDevice = NULL;
  143.     D3DSURFACE_DESC desc;
  144.     memset( &desc, 0, sizeof( desc ) );
  145.     
  146.     // To create the normal map, a clone of the original texture is created (of format D3DFMT_A8R8G8B8)
  147.     // The texture dimensions are needed to clone the texture
  148.     V_RETURN( static_cast<LPDIRECT3DTEXTURE9>(m_pResource)->GetLevelDesc( 0, &desc ) );
  149.  
  150.     V_RETURN( m_pResource->GetDevice( &pDevice ) );
  151.     V( D3DXCreateTexture( pDevice,
  152.                             desc.Width,
  153.                             desc.Height,
  154.                             D3DX_DEFAULT,
  155.                             0,
  156.                             D3DFMT_A8R8G8B8,
  157.                             D3DPOOL_MANAGED,
  158.                             &m_pNormalMap ) );
  159.  
  160.     SAFE_RELEASE( pDevice );
  161.  
  162.     if( FAILED( hr ) )
  163.         return hr;
  164.  
  165.     // The texture clone can now be used to hold the normal map
  166.     V_RETURN( D3DXComputeNormalMap( m_pNormalMap,
  167.                                     static_cast<LPDIRECT3DTEXTURE9>(m_pResource),
  168.                                     NULL,
  169.                                     0,
  170.                                     D3DX_CHANNEL_RED,
  171.                                     10.f ) );
  172.     
  173.     *ppTexture = m_pNormalMap;
  174.     return hr;
  175. }
  176.  
  177.  
  178. //--------------------------------------------------------------------------------------
  179. // CEffect constructor
  180. // When each effect is loaded, several D3DXHANDLEs to common scene-level parameters are
  181. // cached.  This allows a general scene interface (such as CEffect::SetMatrices) to be
  182. // used across all D3DX Effects in the application.
  183. //--------------------------------------------------------------------------------------
  184. CEffect::CEffect( const wstring tsName, const LPD3DXEFFECT pEffect )
  185.     : CResource<LPD3DXEFFECT>( tsName, pEffect )
  186. {
  187.     // Effects may not require all of the following matrices.
  188.     // Prior to updating the transformation matrices, the D3DXHANDLE value will
  189.     // be checked to determine whether or not it was declared by the effect.
  190.     m_hMatWorld      = m_pResource->GetParameterBySemantic( NULL, "WORLD" );
  191.     m_hMatView       = m_pResource->GetParameterBySemantic( NULL, "VIEW" );
  192.     m_hMatViewInv    = m_pResource->GetParameterBySemantic( NULL, "VIEWINV" );
  193.     m_hMatViewProj   = m_pResource->GetParameterBySemantic( NULL, "VIEWPROJECTION" );
  194.     m_hMatProjection = m_pResource->GetParameterBySemantic( NULL, "PROJECTION" );
  195.  
  196.     // Used to avoid redundantly setting view/projection matrices -- ensure that it does not start at 0
  197.     m_nFrameTimestamp = (UINT)-1;
  198.  
  199.     // The first technique that is valid is used.
  200.     // This assumes that multiple materials are not packed into the same effect resource.
  201.     // The intention to represent each unique material type with one effect resource.
  202.     // Within the effect resource, techniques (including fallback methods) are ordered
  203.     // in decreasing order of hardware requirements.  Thus, selecting the first valid
  204.     // technique within the effect will chose the most advanced technique that is
  205.     // supported by the underlying hardware.
  206.     D3DXHANDLE   hTechnique;
  207.     if( FAILED( m_pResource->FindNextValidTechnique( NULL, &hTechnique ) ) )
  208.         throw exception( "ID3DXEffect::FindNextValidTechnique failed" );
  209.  
  210.     // It is possible for FindNextValidTechnique to succeed yet return NULL
  211.     // This would indicate no valid techniques were found.
  212.     if( NULL == hTechnique )
  213.         throw exception( "No Valid Techniques in Effect" );
  214.  
  215.     // Prior to rendering from the effect, the desired technique must be set
  216.     if( FAILED( m_pResource->SetTechnique( hTechnique ) ) )
  217.         throw exception( "ID3DXEffect::SetTechnique failed" );
  218. }
  219.  
  220.  
  221. //--------------------------------------------------------------------------------------
  222. // Creates and loads a D3DX Effect from the specified filename
  223. // If an effect by the same name has already been loaded, it will be returned and its
  224. // reference count incremented.
  225. // Otherwise, the effect will be created and loaded.
  226. //--------------------------------------------------------------------------------------
  227. HRESULT CEffect::Create( LPDIRECT3DDEVICE9 pDevice, LPCWSTR wszFilename, CEffect** ppEffect )
  228. {
  229.     HRESULT hr;
  230.  
  231.     assert( ppEffect );
  232.     *ppEffect = NULL;
  233.  
  234.     // getExisting() will return any previously-loaded instance of this resource,
  235.     // with it's reference count incremented.
  236.     if( CEffect* pEffect = static_cast<CEffect*>( getExisting( wszFilename ) ) )
  237.     {
  238.         *ppEffect = pEffect;
  239.         return S_OK;
  240.     }
  241.  
  242.     // The effect has not yet been loaded -- locate the file
  243.     WCHAR wszPath[MAX_PATH];
  244.     V_RETURN( DXUTFindDXSDKMediaFileCch( wszPath, MAX_PATH, wszFilename ) );
  245.  
  246.     LPD3DXEFFECT pd3dxEffect = NULL;
  247.     LPD3DXBUFFER lpd3dxbufferErrors = NULL;
  248.     
  249.     // Instantiate the effect
  250.     V( D3DXCreateEffectFromFile( pDevice, 
  251.                                 wszPath,
  252.                                 NULL,
  253.                                 NULL,
  254.                                 D3DXSHADER_NO_PRESHADER,
  255.                                 NULL,
  256.                                 &pd3dxEffect,
  257.                                 &lpd3dxbufferErrors ) );
  258.  
  259.     // If there are errors, notify the users
  260.     if( FAILED( hr ) && lpd3dxbufferErrors )
  261.     {
  262.         WCHAR wsz[256];
  263.         MultiByteToWideChar( CP_ACP, 0, (LPSTR)lpd3dxbufferErrors->GetBufferPointer(), -1, wsz, 256 );
  264.         wsz[ 255 ] = 0;
  265.         DXUTTrace( __FILE__, (DWORD)__LINE__, E_FAIL, wsz, true );
  266.     }
  267.  
  268.     // Release and error buffer that was returned
  269.     SAFE_RELEASE( lpd3dxbufferErrors );
  270.  
  271.     if( FAILED( hr ) )
  272.         return E_FAIL;
  273.  
  274.     // Create a CEffect resource type
  275.     // The CEffect class will be responsible for tracking this effect,
  276.     // such that it can be notified during lost device, etc events
  277.     *ppEffect = new CEffect( wszFilename, pd3dxEffect );
  278.  
  279.     return NULL != *ppEffect ? S_OK : E_OUTOFMEMORY;
  280. }
  281.  
  282.  
  283. //--------------------------------------------------------------------------------------
  284. // Effect Lost-Device Entry Point
  285. // This entry point allows D3DX Effects to take actions necessary prior to a Device-Reset,
  286. // such as calling the ID3DXEffect::OnLostDevice() member.
  287. // Following a lost-device event, each effect within the map of loaded effects
  288. // (CResource::m_mapLoaded) will have this entry point called.
  289. // CResource::LostDevice() is responsible for enumerating each effect and calling this
  290. // entry point.
  291. //--------------------------------------------------------------------------------------
  292. HRESULT CEffect::OnLostDevice()
  293. {
  294.     return m_pResource->OnLostDevice();
  295. }
  296.  
  297.  
  298. //--------------------------------------------------------------------------------------
  299. // Effect Reset-Device Entry Point
  300. // This entry point allows D3DX Effects to take actions necessary following a Device-Reset,
  301. // such as calling the ID3DXEffect::OnResetDevice() member.
  302. // Following a reset-device event, each effect within the map of loaded effects
  303. // (CResource::m_mapLoaded) will have this entry point called.
  304. // CResource::ResetDevice() is responsible for enumerating each effect and calling this
  305. // entry point.
  306. //--------------------------------------------------------------------------------------
  307. HRESULT CEffect::OnResetDevice()
  308. {
  309.     return m_pResource->OnResetDevice();
  310. }
  311.  
  312.  
  313. //--------------------------------------------------------------------------------------
  314. // Sets the state manager across all effects
  315. //--------------------------------------------------------------------------------------
  316. HRESULT CEffect::SetStateManager( LPD3DXEFFECTSTATEMANAGER pManager )
  317. {
  318.     bool bResult = true;
  319.  
  320.     // Iterate through the list of loaded effects
  321.     CResource<LPD3DXEFFECT>::resourceMap::iterator it;
  322.     for( it = CResource<LPD3DXEFFECT>::m_mapLoaded.begin();
  323.         it != CResource<LPD3DXEFFECT>::m_mapLoaded.end();
  324.         it++ )
  325.  
  326.         // Set the state manager for this effect
  327.         bResult &= SUCCEEDED( (*it).second->getPointer()->SetStateManager( pManager ) );
  328.  
  329.     return bResult ? S_OK : E_FAIL;
  330. }
  331.  
  332.  
  333. //--------------------------------------------------------------------------------------
  334. // Allows the effect to be updated with scene-level information (the active
  335. // transformation matrices).
  336. //--------------------------------------------------------------------------------------
  337. HRESULT CEffect::SetMatrices( const D3DXMATRIX* pWorld,
  338.                               const D3DXMATRIX* pView,
  339.                               const D3DXMATRIX* pProjection,
  340.                               UINT nFrameTimestamp )
  341. {
  342.     bool bResult = true;
  343.  
  344.     // Avoid updating the view unless needed
  345.     // The view and projection matrices in this sample do not vary between objects
  346.     if( m_nFrameTimestamp != nFrameTimestamp )
  347.     {
  348.         // Update the effect timestamp to avoid setting view/projection matrices until next frame
  349.         m_nFrameTimestamp = nFrameTimestamp;
  350.  
  351.         // If the effect required the projection matrix, update it
  352.         if( m_hMatProjection )
  353.             bResult &= SUCCEEDED( m_pResource->SetMatrix( m_hMatProjection, pProjection ) );
  354.  
  355.         // If the effect required the view matrix, update it
  356.         if( m_hMatView )
  357.             bResult &= SUCCEEDED( m_pResource->SetMatrix( m_hMatView, pView ) );
  358.  
  359.         // If this effect required the contatenated view-proj matrix, it must be computed
  360.         // and updated
  361.         if( m_hMatViewProj )
  362.         {
  363.             D3DXMATRIXA16 matViewProj;
  364.             D3DXMatrixMultiply( &matViewProj, pView, pProjection );
  365.             bResult &= SUCCEEDED( m_pResource->SetMatrix( m_hMatViewProj, &matViewProj ) );
  366.         }
  367.  
  368.         // If this effect requires the true inverse of the view matrix, compute and update it
  369.         if( m_hMatViewInv )
  370.         {
  371.             D3DXMATRIXA16 matViewInv;
  372.             D3DXMatrixInverse( &matViewInv, NULL, pView );
  373.             bResult &= SUCCEEDED( m_pResource->SetMatrix( m_hMatViewInv, &matViewInv ) );
  374.         }
  375.     }
  376.  
  377.     // If the effect required the world matrix, update it
  378.     if( m_hMatWorld )
  379.         bResult &= SUCCEEDED( m_pResource->SetMatrix( m_hMatWorld, pWorld ) );
  380.  
  381.     return bResult ? S_OK : E_FAIL;
  382. }
  383.  
  384.  
  385. //--------------------------------------------------------------------------------------
  386. // Creates and loads a D3DX Mesh from the specified filename
  387. // If a mesh by the same name has already been loaded, it will be returned and its
  388. // reference count incremented.
  389. // Otherwise, the mesh will be created and loaded.
  390. //--------------------------------------------------------------------------------------
  391. HRESULT CMeshObject::Create( LPDIRECT3DDEVICE9 pDevice, LPCWSTR wszFilename, CMeshObject** ppMeshObject )
  392. {
  393.     HRESULT hr;
  394.     assert( ppMeshObject );
  395.     *ppMeshObject = NULL;
  396.  
  397.     // getExisting() will return any previously-loaded instance of this resource,
  398.     // with it's reference count incremented.
  399.     if( CMeshObject* pMeshObject = static_cast<CMeshObject*>( getExisting( wszFilename ) ) )
  400.     {
  401.         *ppMeshObject = pMeshObject;
  402.         return S_OK;
  403.     }
  404.  
  405.     // Locate the media file
  406.     WCHAR wszPath[MAX_PATH];
  407.     V_RETURN( DXUTFindDXSDKMediaFileCch( wszPath, MAX_PATH, wszFilename ) );
  408.  
  409.     // Now that the media file has been found, it can be loaded.
  410.     // Start with a system memory mesh, which will be cloned into a managed mesh.
  411.     // Cloning will add tangent space to the mesh for effects that require it.
  412.     LPD3DXMESH pMesh = NULL;
  413.     LPD3DXBUFFER bufEffectInstances = NULL;
  414.     LPD3DXBUFFER bufAdjacency = NULL;
  415.     DWORD dwMaterials = 0;
  416.  
  417.     V_RETURN( D3DXLoadMeshFromX( wszPath,
  418.                                 D3DXMESH_SYSTEMMEM,
  419.                                 pDevice,
  420.                                 &bufAdjacency,
  421.                                 NULL,
  422.                                 &bufEffectInstances,
  423.                                 &dwMaterials,
  424.                                 &pMesh
  425.                                 ) );
  426.  
  427.     // Optimize the mesh for performance
  428.     V( pMesh->OptimizeInplace(
  429.                     D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE,
  430.                     (DWORD*)bufAdjacency->GetBufferPointer(), NULL, NULL, NULL ) );
  431.  
  432.     // Size of D3DDECLTYPEs -- see d3d9types.h
  433.     static const WORD DeclTypeSizes[] = 
  434.     { 4, 8, 12, 16, 4, 4, 4, 8, 4, 4, 8, 4, 8, 4, 4, 4, 8, };
  435.  
  436.     D3DVERTEXELEMENT9 DeclTangent = { 0, 32, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 };
  437.     D3DVERTEXELEMENT9 DeclEnd = D3DDECL_END();
  438.     vector<D3DVERTEXELEMENT9> vecDecl(MAX_FVF_DECL_SIZE);
  439.     vector<D3DVERTEXELEMENT9>::iterator it_end;
  440.  
  441.     if( SUCCEEDED( hr ) )
  442.     {
  443.         // Obtain the original declaration of the mesh
  444.         V( pMesh->GetDeclaration( &*vecDecl.begin() ) );
  445.     }
  446.  
  447.     LPD3DXMESH pMesh2 = NULL;
  448.     if( SUCCEEDED( hr ) )
  449.     {
  450.         // Locate the last element in the declaration
  451.         if( vecDecl.end() != (it_end = find( vecDecl.begin(), vecDecl.end(), DeclEnd ) )
  452.             && vecDecl.begin() != it_end )
  453.         {
  454.             // Back up to the previous element
  455.             --it_end;
  456.  
  457.             // Position the new element -- determine the offset of the previous element
  458.             WORD wOffset = (*it_end).Offset;
  459.             
  460.             // add the size of the previous element to it's offset
  461.             wOffset += (*it_end).Type < (sizeof DeclTypeSizes / sizeof DeclTypeSizes[0]) 
  462.                 ?  DeclTypeSizes[ (*it_end).Type ] : 0;
  463.             
  464.             // Tangent can go here
  465.             DeclTangent.Offset = wOffset;
  466.  
  467.             // Move forward to the insertion point
  468.             it_end++;
  469.  
  470.             // insert the tangent declaration element
  471.             vecDecl.insert( it_end, DeclTangent );
  472.         }
  473.  
  474.         // Now, clone the entire mesh with tangent
  475.         V( pMesh->CloneMesh(D3DXMESH_MANAGED, &*vecDecl.begin(), pDevice, &pMesh2) );
  476.         SAFE_RELEASE( pMesh );
  477.     }
  478.  
  479.     if( SUCCEEDED( hr ) )
  480.     {
  481.         // Create a CMeshObject resource type
  482.         // The CMeshObject class will be responsible for tracking this mesh,
  483.         // such that it can be notified during lost device, etc events
  484.         *ppMeshObject = new CMeshObject( pDevice,
  485.                                         wszFilename,
  486.                                         pMesh2,
  487.                                         (D3DXEFFECTINSTANCE*)bufEffectInstances->GetBufferPointer(),
  488.                                         dwMaterials );
  489.  
  490.         if( NULL == *ppMeshObject )
  491.             hr = E_OUTOFMEMORY;
  492.     }
  493.  
  494.     SAFE_RELEASE( bufEffectInstances );
  495.     SAFE_RELEASE( bufAdjacency );
  496.  
  497.     return hr;
  498. }
  499.  
  500.  
  501. //--------------------------------------------------------------------------------------
  502. // Following the sucessful load of a new mesh, a material is created for each mesh
  503. // material subset.
  504. //--------------------------------------------------------------------------------------
  505. CMeshObject::CMeshObject( LPDIRECT3DDEVICE9 pDevice,
  506.                           wstring tsName,
  507.                           const LPD3DXMESH pMesh,
  508.                           D3DXEFFECTINSTANCE* pEffectInstance,
  509.                           DWORD dwMaterials )
  510.                           : CResource< LPD3DXMESH>( tsName, pMesh )
  511. {
  512.     while( dwMaterials-- )
  513.     {
  514.         CMaterial* pMaterial;
  515.         
  516.         if( NULL == (pMaterial = new CMaterial( pDevice, pEffectInstance ) ) )
  517.             throw bad_alloc();
  518.  
  519.         m_vecMaterialList.push_back( pMaterial );
  520.         pEffectInstance++;
  521.     }
  522. }
  523.  
  524.  
  525. //--------------------------------------------------------------------------------------
  526. // Following the release of a mesh, its associated materials (allocated in the
  527. // CMeshObject constructor) must be destroyed.
  528. //--------------------------------------------------------------------------------------
  529. CMeshObject::~CMeshObject( )
  530. {
  531.     while( !m_vecMaterialList.empty() )
  532.     {
  533.         CMaterial* pMaterial = m_vecMaterialList.back();
  534.         m_vecMaterialList.pop_back();
  535.         delete pMaterial;
  536.     }
  537. }
  538.  
  539.  
  540. //--------------------------------------------------------------------------------------
  541. // Mesh Lost-Device Entry Point
  542. // This entry point allows D3DX Meshes to take actions necessary prior to a Device-Reset,
  543. // such releasing any D3DPOOL_DEFAULT resources.
  544. // Following a lost-device event, each mesh within the map of loaded meshes
  545. // (CResource::m_mapLoaded) will have this entry point called.
  546. // CResource::LostDevice() is responsible for enumerating each mesh and calling this
  547. // entry point.
  548. //--------------------------------------------------------------------------------------
  549. HRESULT CMeshObject::OnLostDevice()
  550. {
  551.     // D3DPOOL_MANAGED meshes need not implement special lost device handling
  552.     return S_OK;
  553. }
  554.  
  555.  
  556. //--------------------------------------------------------------------------------------
  557. // Mesh Reset-Device Entry Point
  558. // This entry point allows meshes to take actions necessary following a Device-Reset,
  559. // such as re-creating any D3DPOOL_DEFAULT resources.
  560. // Following a reset-device event, each mesh within the map of loaded meshes
  561. // (CResource::m_mapLoaded) will have this entry point called.
  562. // CResource::ResetDevice() is responsible for enumerating each mesh and calling this
  563. // entry point.
  564. //--------------------------------------------------------------------------------------
  565. HRESULT CMeshObject::OnResetDevice()
  566. {
  567.     // D3DPOOL_MANAGED meshes need not implement special reset handling
  568.     return S_OK;
  569. }
  570.  
  571.  
  572. //--------------------------------------------------------------------------------------
  573. // On creation of a new material, the required effect is loaded, and an effect
  574. // instance is created to hold parameter values for the effect.
  575. //--------------------------------------------------------------------------------------
  576. CMaterial::CMaterial( LPDIRECT3DDEVICE9 pDevice, D3DXEFFECTINSTANCE* pEffectInstance )
  577.     : m_pEffect( NULL ), m_pEffectInstance( NULL )
  578. {
  579.     // The D3DXEFFECTINSTANCE structure contains pointers to ANSI strings.
  580.     // The Effect filename is converted prior to usage.
  581.     WCHAR wszEffectFile[MAX_PATH];
  582.     MultiByteToWideChar( CP_ACP, 0, pEffectInstance->pEffectFilename, -1, wszEffectFile, MAX_PATH );
  583.     wszEffectFile[ MAX_PATH -1 ] = 0;
  584.  
  585.     // If the effect file has been previously loaded, CEffect::Create will return the previously loaded
  586.     // effect (after incrementing it's ref count).
  587.     if( FAILED( CEffect::Create( pDevice, wszEffectFile, &m_pEffect ) ) )
  588.         throw exception( "CEffect::Create failed" );
  589.  
  590.     // Even if re-using a previously loaded effect, an effect instance is required to hold the unique
  591.     // parameters for this material
  592.     if(NULL == (m_pEffectInstance = new CEffectInstance( pEffectInstance, m_pEffect->getPointer() ) ) )
  593.         throw bad_alloc();
  594.  
  595.     // The effect instance tells is which texture filenames are required, but does not load them.
  596.     // The material must obtain the list of materials from the effect instance class and load them
  597.     // itself.
  598.     const CEffectInstance::texture_annotations& annotations = m_pEffectInstance->getTextureAnnotations();
  599.     
  600.     for( CEffectInstance::texture_annotations::const_iterator it = annotations.begin();
  601.         it != annotations.end();
  602.         it++ )
  603.     {
  604.         // Create the texture (if previously loaded, CTexture::Create will return the
  605.         // previously loaded instance and increment its ref count).
  606.         CTexture* pTexture = NULL;
  607.         if( FAILED( CTexture::Create( pDevice, (*it).second.c_str(), &pTexture ) ) )
  608.             throw exception( "CTexture::Create Failed" );
  609.         
  610.         // The effect instance must be notified with the underlying D3D texture resource
  611.         // pointer.  This will allow CEffectInstance::apply to update the effect with the
  612.         // correct texture prior to rendering.
  613.         if( FAILED( m_pEffectInstance->addTexture( (*it).first, pTexture ) ) )
  614.             throw exception( "CEffectInstance::addTexture failed" );
  615.         
  616.         // The texture will need to be released when no longer needed by the material (eg,
  617.         // when the CMaterial destructor executes).  This requires tracking the texture
  618.         // until release time.
  619.         m_vecTextures.push_back( pTexture );
  620.     }
  621. }
  622.  
  623.  
  624. //--------------------------------------------------------------------------------------
  625. // When the material is destroyed (due to the destruction of the mesh that created it)
  626. // all resources that it created (see the CMaterial constructor) must be released
  627. //--------------------------------------------------------------------------------------
  628. CMaterial::~CMaterial()
  629. {
  630.     SAFE_RELEASE( m_pEffect );
  631.     
  632.     // Each texture in the m_vecTextures container is released, and removed from the
  633.     // container, until the container is empty.
  634.     while( !m_vecTextures.empty() )
  635.     {
  636.         CTexture* pTexture = m_vecTextures.back();
  637.         m_vecTextures.pop_back();
  638.         SAFE_RELEASE( pTexture );
  639.     }
  640.     
  641.     SAFE_DELETE( m_pEffectInstance );
  642. }
  643.  
  644.  
  645. //--------------------------------------------------------------------------------------
  646. // The D3DXEFFECTINSTANCE structure (obtained from the mesh) must be parsed, and
  647. // individual parameter values for this material are stored in the CEffectInstance
  648. // class.
  649. // Parsing the D3DXEFFECTINSTANCE structure occurs here, in the constructor.  Additionally,
  650. // the CEffectInstance class is responsible for determining the desired technique from the
  651. // effect file, which also is done here.
  652. //--------------------------------------------------------------------------------------
  653. CEffectInstance::CEffectInstance( const D3DXEFFECTINSTANCE* pEffectInstance, LPD3DXEFFECT pEffect )
  654.     : m_pEffect( pEffect )
  655. {
  656.     // The CEffectInstance class caches D3DXHANDLE values within the effect, such that
  657.     // parameter values can be easily loaded into the effect at render time.
  658.     // Should the D3DX Effect resource be unloaded and recreated, the handles would need
  659.     // to be re-cached from the new instance of the effect resource.  To enforce this, 
  660.     // the Effect Pointer is also stored by the Effect Instance and it's reference count
  661.     // is incremented.  To unload the effect, all associated instances of 
  662.     // CEffectInstance must be destroyed.
  663.     m_pEffect->AddRef();
  664.  
  665.     // This loop is where parameter values are parsed out of the D3DXEFFECTDEFAULT structure,
  666.     // which was obtained when the mesh was loaded.
  667.     LPD3DXEFFECTDEFAULT pDefaults = pEffectInstance->pDefaults;
  668.     for( DWORD dw = 0; dw < pEffectInstance->NumDefaults; dw++ )
  669.     {
  670.         // The only string parameters that are supported are texture filenames, which
  671.         // (for this sample) are loaded in the form of Texture#@Name, where # indicates the
  672.         // intended texture stage.
  673.         if( D3DXEDT_STRING == pDefaults->Type)
  674.         {
  675.             string value( (LPSTR)pDefaults->pValue, (LPSTR)pDefaults->pValue + pDefaults->NumBytes );
  676.             string tsName(pDefaults->pParamName);
  677.             if( string::npos != tsName.find( "Texture" ) && string::npos != tsName.find( "@Name" ) )
  678.             {
  679.                 // A texture annotaion was found
  680.                 // convert the texture name to WCHARs from ANSI
  681.                 // The texture loader (CTexture::Create) does not accept ANSI strings.
  682.                 WCHAR wszTexture[MAX_PATH];
  683.                 MultiByteToWideChar( CP_ACP, 0, value.c_str(), -1, wszTexture, MAX_PATH );
  684.                 wszTexture[ MAX_PATH -1 ] = 0;
  685.                 
  686.                 // Add texture filename to the texture annotaions container
  687.                 m_textureAnnotations.insert( texture_annotations::value_type( tsName, wszTexture ) );
  688.             }
  689.             pDefaults++;
  690.             continue;
  691.         }
  692.  
  693.         // Add a new value parameter
  694.         setValue( string(pDefaults->pParamName), (BYTE*)pDefaults->pValue, pDefaults->NumBytes );
  695.         
  696.         pDefaults++;
  697.     }
  698. }
  699.  
  700.  
  701. //--------------------------------------------------------------------------------------
  702. // Release the reference count (held on the D3DX Effect) that was AddRef'd in the
  703. // CEffectInstance constructor
  704. //--------------------------------------------------------------------------------------
  705. CEffectInstance::~CEffectInstance( )
  706. {
  707.     m_pEffect->Release();
  708. }
  709.  
  710.  
  711. //--------------------------------------------------------------------------------------
  712. // This entry point is called to map an Effect Texture parameter to a D3D Texture
  713. // that was loaded from the filename specified in the texture annotation.
  714. //--------------------------------------------------------------------------------------
  715. HRESULT CEffectInstance::addTexture( string annotation_name, CTexture* pTexture )
  716. {
  717.     string param_name = annotation_name;
  718.     
  719.     // Strip the annotation off the annotaion string -- this determines the name
  720.     // of the effect texture parameter ('Texture#@Name' becomes 'Texture#'.)
  721.     string::size_type annotation_delimeter = param_name.find_last_of( "@" );
  722.     if( string::npos != annotation_delimeter )
  723.         param_name.erase( param_name.begin() + annotation_delimeter, param_name.end() );
  724.  
  725.     // Heightmaps must be converted into normal maps before use
  726.     // Determine if the effect requires a normal map
  727.     // If the effect texture parameter contains an annotation "NormalMap", convert to a normal map
  728.     LPDIRECT3DBASETEXTURE9 pTexturePtr = NULL;
  729.     D3DXHANDLE hP;
  730.     if( NULL != (hP = m_pEffect->GetParameterByName( NULL, param_name.c_str() ) )
  731.         && NULL != m_pEffect->GetAnnotationByName( hP, "NormalMap" ) )
  732.     {
  733.         HRESULT hr;
  734.         V_RETURN( pTexture->GetNormalMapPtr( &pTexturePtr ) );
  735.     }
  736.     else
  737.     {
  738.         pTexturePtr = pTexture->getPointer();
  739.     }
  740.  
  741.     // Add a new "value" to be loaded into the effect when apply() is called.
  742.     // Here, the value is a texture pointer.
  743.     setValue( param_name, (BYTE*)&pTexturePtr, sizeof pTexturePtr );
  744.  
  745.     return S_OK;
  746. }
  747.  
  748.  
  749. //--------------------------------------------------------------------------------------
  750. // The CEffectInstance class is used to set Effect Parameter values that may be unique
  751. // to each material instance.  This action is accomplished via a call to apply() prior
  752. // to rendering.
  753. //--------------------------------------------------------------------------------------
  754. HRESULT CEffectInstance::apply() const
  755. {
  756.     bool bResult = true;
  757.     
  758.     instance_params::const_iterator it;
  759.     
  760.     // All material parameters (originating from the D3DXEFFECTINSTANCE structure obtained
  761.     // during mesh loading) are contained by the m_instanceParams container.  Parameters
  762.     // that did not exist within the effect are not added to this container (see
  763.     // CEffectInstance::setValue).
  764.     for( it = m_instanceParams.begin();
  765.         it != m_instanceParams.end();
  766.         it++ )
  767.     {
  768.         bResult &= SUCCEEDED(
  769.             m_pEffect->SetValue( (*it).first, &*(*it).second.begin(), (UINT)(*it).second.size() )
  770.             );
  771.     }
  772.  
  773.     return bResult ? S_OK : E_FAIL;
  774. }
  775.  
  776.  
  777. //--------------------------------------------------------------------------------------
  778. // Protected member setValue is used to set parameter values within the effect instance.
  779. //--------------------------------------------------------------------------------------
  780. void CEffectInstance::setValue( string name, BYTE* pValue, UINT nSizeInBytes )
  781. {
  782.     instance_value value( pValue, pValue + nSizeInBytes );
  783.  
  784.     // If the parameter is not exposed by the effect, it is ignored.
  785.     D3DXHANDLE h = m_pEffect->GetParameterByName( NULL, name.c_str() );
  786.     
  787.     if( NULL != h )
  788.     {
  789.         // The underlying container type of m_instanceParams is a map.
  790.         // The map container was selected because a map ensures that duplicate parameter
  791.         // entries do not exist.  Furthermore, a map efficiently implements a find()
  792.         // operation, allowing existing items to be located quickly.
  793.         // Because an attempt to insert duplicate items of the same key value (D3DXHANDLE)
  794.         // will fail, supporting the update of an existing key value requires locating
  795.         // the item and manually updating it, as is done below.
  796.         instance_params::iterator it = m_instanceParams.find( h );
  797.         
  798.         if( m_instanceParams.end() != it )
  799.             (*it).second = value;
  800.         else
  801.             m_instanceParams.insert( instance_params::value_type( h, value ) );
  802.     }
  803. }
  804.